unit fAccumulator;

interface

uses
  Winapi.Windows,
  Winapi.Messages,
  System.SysUtils,
  System.Variants,
  System.Classes,
  Vcl.Graphics,
  Vcl.Controls,
  Vcl.Forms,
  Vcl.Dialogs,
  Vcl.StdCtrls,
  Vcl.ExtCtrls,
  Vcl.ComCtrls,

  GLS.SceneViewer,
  GLS.Scene,
  GLS.VectorTypes,
  GLS.Objects,
  GLS.GeomObjects,
  GLS.SkyDome,
  GLS.AsyncTimer,
  GLS.Cadencer,
  GLS.Coordinates,

  GLS.BaseClasses,
  GLS.SimpleNavigation,
  GLS.VectorFileObjects;

type
  TAccumObject = class
  private
    FOnUse: boolean;
//    FObject: TGLCube;
//    FObject: TGLTetrahedron;
    FObject: TGLDodecahedron;
    FID: integer;
    procedure SetOnUse(AValue: boolean);
  public
    constructor Create(AID: integer);
    property OnUse: boolean read FOnUse write SetOnUse;
    property ID: integer read FID write FID;
    destructor Destroy; override;
  end;

  TfrmMain = class(TForm)
    GLScene: TGLScene;
    SceneViewer: TGLSceneViewer;
    camMain: TGLCamera;
    dcObjects: TGLDummyCube;
    dscTerrain: TGLDisk;
    lsLight: TGLLightSource;
    esdSky: TGLEarthSkyDome;
    btnInitAccum: TButton;
    btnAddObject: TButton;
    btnDelObject: TButton;
    btnDeinitAccum: TButton;
    FPSTimer: TGLAsyncTimer;
    pnlFPS: TPanel;
    Cadencer: TGLCadencer;
    GLSimpleNavigation: TGLSimpleNavigation;
    StatusBar1: TStatusBar;
    procedure FormResize(Sender: TObject);
    procedure btnInitAccumClick(Sender: TObject);
    procedure btnAddObjectClick(Sender: TObject);
    procedure btnDelObjectClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure btnDeinitAccumClick(Sender: TObject);
    procedure FPSTimerTimer(Sender: TObject);
    procedure CadencerProgress(Sender: TObject;
      const deltaTime, newTime: Double);
  private
  public
  end;

const
  MAX_OBJECTS = 100;

var
  frmMain: TfrmMain;
  Objects: array [0 .. MAX_OBJECTS - 1] of TAccumObject;
  AccumInit: boolean = false;

//=====================================================
implementation
//=====================================================

{$R *.dfm}

function PlusMinusRandom(AValue: Single): Single;
begin
  if Random(100) <= 50 then
    Result := Random(Trunc(AValue * 100)) / 100
  else
    Result := -Random(Trunc(AValue * 100)) / 100;
end;

constructor TAccumObject.Create;
begin
//  FObject := TGLCube.CreateAsChild(frmMain.dcObjects);
//  FObject := TGLTetrahedron.CreateAsChild(frmMain.dcObjects);
  FObject := TGLDodecahedron.CreateAsChild(frmMain.dcObjects);
  FOnUse := false;

//  FObject.CubeDepth := 0.5;
  FObject.Scale.X := 0.5;
//  FObject.CubeHeight := 0.1;
  FObject.Scale.Y := 0.5;
//  FObject.CubeWidth := 0.1;
  FObject.Scale.Z  := 0.5;
  FObject.Visible := false;
end;

procedure TAccumObject.SetOnUse(AValue: boolean);
begin
  if AValue <> FOnUse then
    FOnUse := AValue;
  FObject.Visible := FOnUse;
end;

destructor TAccumObject.Destroy;
begin
  FreeAndNil(FObject);
  inherited Destroy;
end;

//------------------------------------------------
function GetNonUseObject: TAccumObject;
var
  i: integer;
begin
  Result := nil;
  for i := 0 to MAX_OBJECTS - 1 do
    if not Objects[i].OnUse then
    begin
      Result := Objects[i];
      Objects[i].FObject.Position.X := PlusMinusRandom(0.8);
      Objects[i].FObject.Position.Y := PlusMinusRandom(0.8);
      Result.OnUse := true;
      Break;
    end;
end;

function KillUseObject: integer;
var
  i: integer;
begin
  for i := 0 to MAX_OBJECTS - 1 do
    if Objects[i].OnUse then
    begin
      Objects[i].OnUse := false;
      Result := i;
      Break;
      Exit;
    end
    else
      Result := -1;
end;

procedure TfrmMain.FormResize(Sender: TObject);
begin
  camMain.SceneScale := SceneViewer.Width / 666;
end;

procedure TfrmMain.btnInitAccumClick(Sender: TObject);
var
  i: integer;
begin
  if AccumInit then
  begin
    Application.MessageBox('Accumulator was initialized!', PChar(Caption), 0);
    Exit;
  end;
  for i := 0 to MAX_OBJECTS - 1 do
    Objects[i] := TAccumObject.Create(i);
  AccumInit := true;
end;

procedure TfrmMain.btnAddObjectClick(Sender: TObject);
begin
  if not AccumInit then
  begin
    Application.MessageBox('Accumulator not initialized!', PChar(Caption), 0);
    Exit;
  end;
  if GetNonUseObject = nil then
    Application.MessageBox('There are no free objects in accumulator!', PChar(Caption), 0);
end;

procedure TfrmMain.btnDelObjectClick(Sender: TObject);
begin
  if not AccumInit then
  begin
    Application.MessageBox('Accumulator not initialized!', PChar(Caption), 0);
    Exit;
  end;
  if KillUseObject = -1 then
    Application.MessageBox('There are no used objects in accumulator!',
      PChar(Caption), 0);
end;

procedure TfrmMain.FormCreate(Sender: TObject);
begin
  Randomize;
  FPSTimer.OnTimer(FPSTimer);
end;

procedure TfrmMain.btnDeinitAccumClick(Sender: TObject);
var
  i: integer;
begin
  if not AccumInit then
  begin
    Application.MessageBox('Accumulator not initialized!', PChar(Caption), 0);
    Exit;
  end;
  for i := 0 to MAX_OBJECTS - 1 do
    Objects[i].Free;
  AccumInit := false;
end;

procedure TfrmMain.FPSTimerTimer(Sender: TObject);
begin
  pnlFPS.Caption := Format('%0.f FPS', [SceneViewer.FramesPerSecond]);
  SceneViewer.ResetPerformanceMonitor;
end;

procedure TfrmMain.CadencerProgress(Sender: TObject;
  const deltaTime, newTime: Double);
begin
  SceneViewer.Invalidate;
end;

end.
